package com.lagou.edu.users.config;

import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

@Data
@Component
@RefreshScope
public class FlushBrushPathBean {
    private static final int DEFAULT_LIMIT_TIME_MINUTUE = 1;
    private static final int DEFAULT_LIMIT_COUNT = 1000000;
    private long expire = 2 * 60 * 1000 * DEFAULT_LIMIT_TIME_MINUTUE; //清除时间
    /**
     * 防刷爆时间范围,单位分钟
     */
    @Value("${flush.brush.limitTimeMinute}")
    private Integer limitTimeMinute;

    /**
     * 防刷爆次数
     */
    @Value("${flush.brush.limitCount}")
    private Integer limitCount;

    private double rate = (double) DEFAULT_LIMIT_COUNT / (DEFAULT_LIMIT_TIME_MINUTUE * 60);
    private long capacity = DEFAULT_LIMIT_COUNT * 1000; //容量
    private long lastCleanTime; //最后清除时间

    private Map<String,Long> requestCountMap = new HashMap<>(); //记录漏桶装载量
    private Map<String,Long> requestTimeMap = new HashMap<>(); //记录请求时间

    private Lock lock = new ReentrantLock(true);

    public void reflush(){
        if (limitTimeMinute <= 0 || limitCount <= 0) {
            throw new IllegalArgumentException();
        }
        this.capacity = limitCount * 60 * 1000;
        this.rate = (double) limitCount / (limitTimeMinute * 60);
        this.expire = 2 * 60 * 1000 * limitTimeMinute;
    }

    /**
     * 漏桶算法
     * 利用漏桶算法，进行方法调用限流
     */
    public boolean isGranted(String hostAddr){
        try {
            lock.lock();
            reflush();
            long current = System.currentTimeMillis();
            cleanUp(current); // 清除记录
            Long lastRequestTime = requestTimeMap.get(hostAddr);
            long count = 0;
            if (lastRequestTime == null){
                count += 60 * 1000;
                requestTimeMap.put(hostAddr,current);
                requestCountMap.put(hostAddr,count);
                return true;
            } else {
                count = requestCountMap.get(hostAddr);
                long useTime = current - lastRequestTime;
                count -= (useTime) * rate; //漏水
                count = count > 0 ? count : 0;
                requestTimeMap.put(hostAddr , current);
                if (count < capacity){
                    //容量未满,装水
                    count += 60 * 1000;
                    requestCountMap.put(hostAddr , count);
                    return true;
                }else {
                    // 容量已满
                    requestCountMap.put(hostAddr , count);
                    return false;
                }
            }
        }finally {
            lock.unlock();
        }
    }

    private void cleanUp(long current) {
        if (current - lastCleanTime > expire) {
            for (Iterator<Map.Entry<String, Long>> it = requestTimeMap.entrySet().iterator(); it.hasNext();) {
                Map.Entry<String, Long> entry = it.next();
                if (entry.getValue() < current - expire) {
                    it.remove();
                    requestCountMap.remove(entry.getKey());
                }
            }
            lastCleanTime = current;
        }
    }
}
